#pragma once
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signal.hpp>
#include <queue>
#include <iostream>
#include "Common.h"

const int SINGLE_THREAD_POOL_SIZE = 1;

class CAsyncTask : public boost::enable_shared_from_this<CAsyncTask>
{  
public:  
    CAsyncTask(int threadPoolSize);
    virtual ~CAsyncTask(); 

public:    
    virtual bool Start();
    virtual void Stop();

    class CQueuedHandler {
    public:
        CQueuedHandler(int p, boost::function<void()> f)
                : m_priority(p), m_function(f) {
        }
    
        CQueuedHandler() {
        }

        void Execute() {
            m_function();
        }

        friend bool operator<(const CQueuedHandler& a, const CQueuedHandler& b) {
            return a.m_priority < b.m_priority;
        }

        int m_priority;
        boost::function<void()> m_function;
    };

    template <typename Handler>
    void Post(Handler handler, int priority = 0) {
        {
            boost::mutex::scoped_lock lock(m_mutex);
            boost::shared_ptr<CQueuedHandler> queuedHandler(new CQueuedHandler(priority, handler));
            m_priQueue.push(queuedHandler);
        }

        m_ioService->post(boost::bind(&CAsyncTask::ExecHandler, shared_from_this()));
    }

protected:
    void ExecHandler() {
        boost::shared_ptr<CQueuedHandler> queuedHandler; 

        {
            boost::mutex::scoped_lock lock(m_mutex);
            if(!m_priQueue.empty()) {
                queuedHandler = m_priQueue.top();
                m_priQueue.pop();
            }
        }
       
        if(m_ioService->stopped()) {
            return;
        }

        if(queuedHandler.get()) {
            queuedHandler->Execute();
        }
    }

protected:
    mutable boost::mutex m_mutex;
    boost::shared_ptr<boost::asio::io_service> m_ioService;
    boost::scoped_ptr<boost::asio::io_service::work> m_idleWork;
    int m_threadPoolSize;
    std::vector<boost::shared_ptr<boost::thread> > m_workThreadPool;
    std::priority_queue<boost::shared_ptr<CQueuedHandler> > m_priQueue; // priority queue for handler
};    
